package e3dds

import (
	"errors"
	"fmt"

	"gitee.com/ameise84/e3dds/internal/help"
	"gitee.com/ameise84/e3dds/internal/log"
	"gitee.com/ameise84/e3dds/internal/message"
	"gitee.com/ameise84/e3pool/go_pool"
)

func newEntityServiceLocal(isNetService bool, meta meta, fact ActorFactory, opts *ServiceOptions) *entity {
	e := &entity{
		meta:             meta,
		isNetService:     isNetService,
		isLocal:          true,
		opts:             opts,
		protocolHandlers: make(map[string]ProtocolHandler),
		timerHandlers:    make(map[uint16]TimerHandler),
	}
	e.behaviorMan = newBehaviorManager(newActorLocal, e, fact)
	e.runner = go_pool.NewGoRunner(e, "entity runner", go_pool.DefaultOptions())
	return e
}

func newEntityServiceRemote(meta meta, remote *actorRemote) *entity {
	e := &entity{
		meta:    meta,
		isLocal: false,
		remote:  remote,
	}
	e.runner = go_pool.NewGoRunner(e, "entity remote runner", go_pool.DefaultOptions())
	return e
}

type entity struct {
	meta
	isNetService     bool
	isLocal          bool
	behaviorMan      *behaviorManager
	opts             *ServiceOptions
	remote           *actorRemote
	protocolHandlers map[string]ProtocolHandler
	timerHandlers    map[uint16]TimerHandler
	runner           go_pool.GoRunner
}

func (ts *entity) StartNetActor(cid string) (NetBehavior, bool) {
	return ts.behaviorMan.findOrStartActor(cid)
}

func (ts *entity) StopNetActor(cid string) {
	ts.behaviorMan.stopActor(cid, false)
}

func (ts *entity) StartRpcActor(cid string) (Behavior, bool) {
	return ts.behaviorMan.findOrStartActor(cid)
}

func (ts *entity) StopRpcActor(cid string) {
	ts.behaviorMan.stopActor(cid, false)
}

func (ts *entity) RegisterProtocolHandler(v any) {
	RegisterProtoHandler[ProtocolHandler](v, ts.protocolHandlers)
}

func (ts *entity) RegisterProtocolHandlerWithName(protocName string, hand ProtocolHandler) {
	RegisterProtoHandlerWithName[ProtocolHandler](protocName, hand, ts.protocolHandlers)
}

func (ts *entity) RegisterTimerHandler(kind uint16, hand TimerHandler) {
	if _, ok := ts.timerHandlers[kind]; ok {
		panic(fmt.Sprintf("timer handler[%v] already registered", kind))
	}
	ts.timerHandlers[kind] = hand
}

func (ts *entity) isVirtualService() bool {
	return false
}

func (ts *entity) findProtocolHandler(protocName string) (ProtocolHandler, bool) {
	h, ok := ts.protocolHandlers[protocName]
	return h, ok
}

func (ts *entity) findTimerHandler(kind uint16) (TimerHandler, bool) {
	h, ok := ts.timerHandlers[kind]
	return h, ok
}

func (ts *entity) OnPanic(err error) {
	log.GetLogger().Error("OnPanic").Object(ts).Err(err).Println()
}

func (ts *entity) LoadBalancerID() uint64 {
	return help.Hash16(ts.meta.SessionID)
}

func (ts *entity) setState(state stateKind) {
	_, _ = ts.runner.SyncRun(ts.runSetState, state)
}

func (ts *entity) metaToJson() string {
	return ts.toJson()
}

func (ts *entity) isLocalService() bool {
	return ts.isLocal
}

func (ts *entity) getBLMode() BLMode {
	return ts.meta.BLMode
}

func (ts *entity) GetServiceID() string {
	return ts.ServiceID
}

func (ts *entity) getState() stateKind {
	return ts.State
}

func (ts *entity) setSessionID(v string) {
	ts.SessionID = v
}

func (ts *entity) setRpcAddr(v string) {
	ts.RpcAddr = v
}

func (ts *entity) moveActor(cb func(*actorLocal) bool) {
	if ts.isLocal {
		ts.behaviorMan.moveActors(cb)
	}
}

func (ts *entity) close(isMove bool) {
	if ts.isLocal {
		ts.behaviorMan.stopActors(isMove)
	}
}

func (ts *entity) onPost(msg *message.Msg) {
	if err := ts.runner.AsyncRun(ts.runMsgPost, msg); err != nil {
		doMsgPostErr(msg, err)
	}
}

func (ts *entity) onBroadcast(msg *message.Msg) {
	if err := ts.runner.AsyncRun(ts.runMsgBroadcast, msg); err != nil {
		doMsgPostErr(msg, err)
	}
}

func (ts *entity) stopActor(cid string) {
	if ts.isLocal {
		ts.behaviorMan.stopActor(cid, false)
	}
}

func (ts *entity) onActorStopped(cid string) {
	if ts.isLocal {
		ts.behaviorMan.onActorDel(cid)
	}
}

func (ts *entity) getSessionID() string {
	return ts.SessionID
}

func (ts *entity) runSetState(args ...any) (any, error) {
	ts.State = args[0].(uint32)
	log.GetLogger().Info("set service state").Object(ts).Str("state", serviceStateString[ts.State]).Println()
	if ts.State == stateReady {
		//todo 处理undo
	}
	return nil, nil
}

func (ts *entity) runMsgPost(args ...any) {
	msg := args[0].(*message.Msg)
	to := msg.GetToActor()

	if msg.IsResponse() && ts.isLocal {
		a, ok := ts.behaviorMan.findActor(to.ClerkID())
		if ok {
			a.onRecvRpcMsg(msg)
			return
		}
	}

	if ts.State != stateReady {
		_gRouterProvider.postUndo(msg)
		return
	}

	if !ts.isLocal {
		if err := ts.remote.send(msg); err != nil {
			_gRouterProvider.retry(msg, err)
		}
		return
	}

	a, ok := ts.behaviorMan.findOrStartActor(to.ClerkID())
	if ok {
		a.onRecvRpcMsg(msg)
	} else {
		doMsgPostErr(msg, errors.New("actor start failed"))
	}
}

func (ts *entity) runMsgBroadcast(args ...any) {
	msg := args[0].(*message.Msg)
	if !ts.isLocal {
		if err := ts.remote.send(msg); err != nil {
			_gRouterProvider.retry(msg, err)
		}
		return
	}

	if ts.State != stateReady {
		_gRouterProvider.postUndo(msg)
		return
	}

	switch msg.GetKind() {
	case message.MsgBroadcastToActor:
		for id, a := range ts.behaviorMan.actorLocals {
			nMsg := message.TakeMessage()
			nMsg.Clone(msg)
			nMsg.SetToActor(ts.ServiceID, ts.SessionID, id)
			a.onRecvRpcMsg(nMsg)
		}
	case message.MsgBroadcastToService:
		ts.behaviorMan.factory.OnBroadcast(msg)
	default:
	}
	message.FreeMessage(msg)
}

func (ts *entity) wait() {
	ts.runner.Wait()
}
